import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.filechooser.FileFilter;

import org.supercsv.io.CsvBeanReader;
import org.supercsv.io.ICsvBeanReader;
import org.supercsv.prefs.CsvPreference;

/*
 * OnixGBESGALCreator is a standalone application that allows you to selcet a .csv input file, 
 * and after specifying where you would want the output file, converts the user data in the csv file 
 * into an .xml file that is used by the Google Apps Connector for Blackberry Enterprise Server.
 * @author Grant McCarthy
 * 
 * The application also uses the open source SuperCSV by Kasper B. Graversen, 2006-2009, to read the data from
 * the CSV files. This code can be found at http://supercsv.sourceforge.net/
 * 
 * © Onix Networking 2010
 */
public class GBESGALCreator extends JPanel implements ActionListener {
	private JFileChooser fc = new JFileChooser();
	private static File inputFile;
	private static String outputPath;
	private static JTextArea log;
	private static JTextArea inputText;
	private static JTextArea outputText;
	private static GridBagConstraints constraints = new GridBagConstraints();
	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS");
	private JFrame controllingFrame;

/**
 * Constructor takes the JFrame as the argument. The constructor builds all the elements to the application and 
 * adds each element to the frame using the GridBag Layout manager.
 * @param frame
 */
	public GBESGALCreator(JFrame frame)  {
		controllingFrame = frame;
		setLayout(new GridBagLayout());
		constraints.fill = GridBagConstraints.BOTH;
		constraints.weightx = 0.5;
		constraints.weighty = 0.10;
		JButton uploadButton = new JButton("Select input file");
		uploadButton.setActionCommand("input");
		uploadButton.addActionListener(this);
		addGB(uploadButton,0,0);
		inputText = fetchTextArea("Select the source file (.csv)");
		addGB(inputText,1,0);
		JButton outputButton = new JButton("Set XML (output) file");
		outputButton.setActionCommand("output");
		outputButton.addActionListener(this);
		addGB(outputButton,0,1);
		outputText = fetchTextArea("Set the XML (output) file");
		addGB(outputText,1,1);
		JButton convertButton = new JButton("Convert");
		convertButton.setActionCommand("convert");
		convertButton.addActionListener(this);
		constraints.gridwidth = 2;
		constraints.weightx = 1.0;
		addGB(convertButton,0,2);
		constraints.weighty = 0.50;
		constraints.weighty = 1.0;
		addGB(fetchLogPane(),0,3);
		constraints.weighty = 0.05;
		addGB(fetchTextArea("Copyright 2010 Onix Networking Corp"),0,4);
		JMenuBar menubar = new JMenuBar();
		JMenu helpMenu = new JMenu("Help");
		JMenuItem about = new JMenuItem("About");
		about.setActionCommand("about");
		about.addActionListener(this);
		helpMenu.add(about);
		menubar.add(helpMenu);
		controllingFrame.setJMenuBar(menubar);
		
	}
/**
 * This is the method that creates the GUI for the application
 * @throws IOException
 */
	private static void createGUI() throws IOException{
		JFrame.setDefaultLookAndFeelDecorated(false);
		JFrame frame = new JFrame("gBESGALCreator");
		frame.addWindowListener(new ExitListener());
		frame.setSize(500, 300);
		frame.setContentPane(new GBESGALCreator(frame));
//		frame.pack();
		frame.setVisible(true);
	}
	/**
	 * This application takes no arguments
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		createGUI();
	}
	
	/**
	 * This method takes the component, and adds it to the frame at the location specified by the x and y values supplied.
	 * @param component
	 * @param x
	 * @param y
	 */
	private void addGB(Component component, int x, int y){
		constraints.gridx = x;
		constraints.gridy = y;
		add(component, constraints);
		
	}
	
	/**
	 * Sets the input file and displays an error message in the log if there is a problem with the file. If successful, 
	 * the file path is displayed in the inputText text area. 
	 */
	private void showFileUploadDialog() {
		inputFile = fc.getSelectedFile();
		if(inputFile!=null){
			boolean fileExists = inputFile.exists();
			boolean fileCanRead = inputFile.canRead();
			if (!fileExists || !fileCanRead){
				System.out.println("cannot read " + inputFile);
				log.append("\nCannot read " + inputFile);
				return;
			}
			else inputText.setText(inputFile.getAbsolutePath());
		}
	}
	
	/**
	 * Sets the outputPath and displays it in the outputText text area.
	 */
	private void showFileOutputDialog(){
		outputPath = fc.getSelectedFile().getAbsolutePath();
		outputText.setText(outputPath);
	}
	
	/**
	 * Displays a new frame when one clicks on Help - About.
	 */
	private void showAboutPanel(){
		JFrame aboutFrame = new JFrame("About gBESGALCreator");
		aboutFrame.setSize(400,250);
		aboutFrame.setLayout(new BorderLayout());
		JTextPane aboutText = new JTextPane();
		StringBuffer sb = new StringBuffer();
		sb.append("This application was developed in 2010 by Onix Networking, a Google Apps Reseller.\n");
		sb.append("The application will take a Comma Separated Value (CSV) file containing peoples's ");
		sb.append("names, email addresses, work numbers, mobile numbers and fax numbers and ");
		sb.append("generate an XML file for the Google Apps Connector for Blackberry Enterprise Server ");
		sb.append("to use as the Global Address List for the Blackberry devices associated with it.\n ");
		sb.append("For more information, or contact details, please visit us online at http://www.onixnet.com");
		sb.append(" or click on the link below.");
		aboutText.setText(sb.toString());
		aboutText.setEditable(false);
		JHyperlink onixLink = new JHyperlink("Onix Networking","http://www.onixnet.com");
		JPanel onixInfo = new JPanel();
		onixInfo.add(new JScrollPane(new JLabel(createImageIcon("images/logo.gif", "Onix Logo"))));
		onixInfo.add(onixLink);
		aboutFrame.add(new JScrollPane(aboutText), BorderLayout.CENTER);
//		aboutFrame.add(onixLink, BorderLayout.SOUTH);
		aboutFrame.add(onixInfo, BorderLayout.SOUTH);
		aboutFrame.setVisible(true);
	}
	
	/**
	 * This returns the application's log window, and it places an image on the background of the pane
	 * @return
	 */
	private JScrollPane fetchLogPane(){
		log = new JTextArea(){
			ImageIcon imageIcon =createImageIcon("images/logo.gif", "Onix Logo");
			Image image = imageIcon.getImage();
		      {
		        setOpaque(false);
		      }

		      public void paint(Graphics g) {
		        g.drawImage(image, 350, 90, this);
		        super.paint(g);
		      }
		};
		
        log.setMargin(new Insets(5,5,5,5));
        log.setEditable(false);
        JScrollPane logScrollPane = new JScrollPane(log);
        logScrollPane.setSize(new Dimension (0,150));
        logScrollPane.setMinimumSize(new Dimension (0,150));
        logScrollPane.setPreferredSize(new Dimension (0,150));
        return logScrollPane;
	}
	
	/**
	 * A generic method that returns a text area with the text that is supplied 
	 * @param text
	 * @return
	 */
	private JTextArea fetchTextArea(String text){
		JTextArea textArea = new JTextArea(text);
		textArea.setMargin(new Insets(5,5,5,5));
		textArea.setEditable(false);
        return textArea;
	}
	
	/** Returns an ImageIcon, or null if the path was invalid. */
	protected ImageIcon createImageIcon(String path, String description) {
		java.net.URL imgURL = getClass().getResource(path);
		if (imgURL != null) {
			return new ImageIcon(imgURL, description);
		} else {
		    System.err.println("Couldn't find file: " + path);
		    return null;
		}
	}
	
	/**
	 * Creates a FileOpenChooser, and uses the extension provided as a filter. IN this case we only want to read .csv files for input
	 * and .xml for output.
	 * @param extension
	 */
	private void createFileOpenChooser(String extension){
		//Create a file chooser
		fc = new JFileChooser();
        fc.setMultiSelectionEnabled(false);
        FileFilter fileFilter = new ExtensionFileFilter(extension.toUpperCase() + " files", extension);
        fc.setFileFilter(fileFilter);
	}
	
	/**
	 * This is where the data from the CSV file is appended to a specifically formated xml file. We make use of the SuperCSV classes 
	 * to extract the required information from the CSV file. The newly created XML file is then saved where the path specifies.
	 * @param inputFile
	 * @param path
	 */
	private void convertCsvToXml(File inputFile, String path){
		int count = 0;
		Date date = new Date();
		try {
			ICsvBeanReader inFile = new CsvBeanReader(new FileReader(inputFile), CsvPreference.STANDARD_PREFERENCE);
			NewContact newContact;
			try {
				FileWriter fstream = new FileWriter(path);
				BufferedWriter out = new BufferedWriter(fstream);
//				For an XML file to be read correctly by the Google Apps Connector, it must contain the following lines 
				out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
				out.write("<atom:feed");
				out.write("   xmlns=\"http://schemas.google.com/gal/2009\"");
				out.write("   xmlns:atom=\"http://www.w3.org/2005/Atom\"");
				out.write("   xmlns:openSearch=\"http://a9.com/-/spec/opensearchrss/1.0/\"");
				out.write("   xmlns:gd=\"http://schemas.google.com/g/2005\"");
				out.write("   xmlns:gal=\"http://schemas.google.com/gal/2009\">");
			     final String[] header = inFile.getCSVHeader(true);
//			     We loop through each cell in the CSV and extract the necessary information
			     while( (newContact = inFile.read(NewContact.class, header)) != null) {
			    	 int i = newContact.getEmail().indexOf("@");
//			    	 Conforming to Google Apps standards, the user id is the part of the email address before the @ sign
					 String userId = ( i!= -1) ? newContact.getEmail().substring(0, i) : "";
					 String lastName = newContact.getLastName() != null ? newContact.getLastName() : "";
					 String firstName = newContact.getFirstName() != null ? newContact.getFirstName() : "";
//					 if (firstName.indexOf("z")==0) continue;
					 String email = newContact.getEmail() != null ? newContact.getEmail() : "";
					 String officeNumber = newContact.getOfficePhoneNumber() != null ? newContact.getOfficePhoneNumber() : "";
					 String cellNumber = newContact.getCellPhoneNumber() != null ? newContact.getCellPhoneNumber() : "";
					 String faxNumber = (newContact.getOfficeFaxNumber() != null  && !newContact.getOfficeFaxNumber().equals("")) ? "Fax: " + newContact.getOfficeFaxNumber() : "";
//			    	 We now use the data gathered above and place it in the correct format for the GAL to interpret it
					 out.write("<atom:entry>");
			    	 out.write("<atom:id>https://apps-apis.google.com/a/feeds/pintys.com/user/2.0/" + userId +"</atom:id>");
			    	 out.write("	    <atom:published>" + sdf.format(date) +"</atom:published>");
			    	 out.write("    <atom:updated>" + sdf.format(date) +"</atom:updated>");
	    			 out.write("<atom:title type=\"text\">" + lastName + ", " + firstName +"</atom:title>");
					 out.write("	    <gal:type type=\"profile\"/>");
					 out.write("	    <gal:displayname>" + firstName + " " + lastName + "</gal:displayname>");
					 out.write("	    <gal:givenname>" + firstName +"</gal:givenname>");
					 out.write("	    <gal:surname>" + lastName +"</gal:surname>");
					 out.write("		<gal:phone work=\"" + officeNumber + "\" home=\"\" mobile=\"" + cellNumber + "\" other=\""+ faxNumber + "\"/>");
					 out.write("		<gal:email>" + email + "</gal:email>");
					 out.write("		<gal:syncid id=\"0\"/>");
					 out.write("		<gd:deleted>false</gd:deleted>");
					 out.write(" </atom:entry>");
			    	 count++;
			     }
			     out.write(" 		  </atom:feed>");
			     out.close();
			     inputText.setText(inputFile.getPath());
			     log.append("\n" + count + " Contacts added to " + path);
			}  finally {
			      inFile.close();
			   }
			

		} catch (FileNotFoundException ex){
			ex.printStackTrace();
		} catch (IOException e) {
			log.append("There was an I/O error. Please try again.");
			e.printStackTrace();
		} catch (Exception e) {
			log.append("There was an error. Please try again.");
			e.printStackTrace();
		} 
	}


	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getActionCommand().equals("input")){
			createFileOpenChooser("csv");
			int returnVal = fc.showOpenDialog(this);
			if (returnVal == JFileChooser.APPROVE_OPTION){
				showFileUploadDialog();
			}
		}
		else if (e.getActionCommand().equals("output")){
			createFileOpenChooser("xml");
			int returnVal = fc.showSaveDialog(this);
			if (returnVal == JFileChooser.APPROVE_OPTION){
				showFileOutputDialog();
			}
		}
		
		else if (e.getActionCommand().equals("convert")){
			if (outputPath!=null & inputFile!=null){
				convertCsvToXml(inputFile, outputPath);
			}
			else log.append("Either input file or output path has not been set!");
		}
		
		else if(e.getActionCommand().equals("about")){
			showAboutPanel();
		}
	}
}

